#include <stdio.h>
#include <allegro.h>

#define LABEL_SPRITES	1
#define LABEL_ANIMS		2

#define RETURN_EOF		-1
#define RETURN_ERROR	-2
#define RETURN_COMMENT	-3
#define RETURN_M		-4
#define RETURN_M_END	-5

BITMAP *bmp;
FILE *txt;
FILE *psf;

short sprite_count;
short anim_count;
short start_x;
short start_y;
short size_x;
short size_y;

char orientation;	// used to track 'm', 'f', and 'r' values

typedef struct{
	char p;	// palette color
	char r;	// red
	char g;	// green
	char b;	// blue
} SPRITE_SHEET;
SPRITE_SHEET *sheet;

typedef struct{
	short size_x_left;
	short size_x_right;
	short size_y_top;
	short size_y_bottom;
} SPRITE_DATA;
SPRITE_DATA *sprite;

short *anim;



int FindValue(){
	int i;
	short label[5];
	
	orientation = 0;
	
	label[0] = getc(txt);
	while(label[0] == 0x20 || label[0] == 0x9)
		label[0] = getc(txt);
	
	if(label[0] == RETURN_EOF)
		return RETURN_EOF;
	if(label[0] == '#')
		return RETURN_COMMENT;
	
	for(i=0; i < 5; i++){
		if(label[i] >= '0' && label[i] <= '9'){
			label[i] -= '0';
			if(orientation)
				return RETURN_ERROR;
		}
		else if(label[i] >= 'A' && label[i] <= 'F'){
			label[i] -= 'A';
			label[i] += 10;
			if(orientation)
				return RETURN_ERROR;
		}
		/*else if(label[i] >= 'a' && label[i] <= 'f'){
			label[i] -= 'a';
			label[i] += 10;
		}*/
		else if(label[i] == ',' || label[i] == 0xD || label[i] == 0xA){
			label[i] = -1;
			break;
		}
		else if(label[i] == 'm'){
			orientation = 1;
			i--;
		}
		else if(label[i] == 'f'){
			orientation = 2;
			i--;
		}
		else if(label[i] == 'r'){
			orientation = 3;
			i--;
		}
		else
			return RETURN_ERROR;
		
		label[i+1] = getc(txt);
	}
	if(i == 5)
		return RETURN_ERROR;
	
	if(label[0] == -1)
		return 0;
	if(label[1] == -1)
		return label[0];
	if(label[2] == -1)
		return (label[0] << 4) + label[1];
	if(label[3] == -1)
		return (label[0] << 8) + (label[1] << 4) + label[2];
	
	return (label[0] << 12) + (label[1] << 8) + (label[2] << 4) + label[3];
}



int FindSub(){
	int i;
	char label[5];
	
	label[0] = getc(txt);
	while(label[0] == 0x20
	|| label[0] == 0xD
	|| label[0] == 0xA
	|| label[0] == 0x9)
		label[0] = getc(txt);
	
	if(label[0] == RETURN_EOF)
		return RETURN_EOF;
	if(label[0] == '#')
		return RETURN_COMMENT;
	if(label[0] == '&')
		return RETURN_M;
	
	for(i=0; i < 5; i++){
		if(label[i] >= '0' && label[i] <= '9')
			label[i] -= '0';
		else if(label[i] >= 'A' && label[i] <= 'F'){
			label[i] -= 'A';
			label[i] += 10;
		}
		/*else if(label[i] >= 'a' && label[i] <= 'f'){
			label[i] -= 'a';
			label[i] += 10;
		}*/
		else if(label[i] == ':'){
			label[i] = -1;
			break;
		}
		else
			return RETURN_ERROR;
		
		label[i+1] = getc(txt);
	}
	if(i == 5)
		return RETURN_ERROR;
	
	if(label[0] == -1)
		return 0;
	if(label[1] == -1)
		return label[0];
	if(label[2] == -1)
		return (label[0] << 4) + label[1];
	if(label[3] == -1)
		return (label[0] << 8) + (label[1] << 4) + label[2];
	
	return (label[0] << 12) + (label[1] << 8) + (label[2] << 4) + label[3];
}



int FindLabel(){
	char label[9];
	
	label[0] = getc(txt);
	while(label[0] == 0x20
	|| label[0] == 0xD
	|| label[0] == 0xA
	|| label[0] == 0x9){
		label[0] = getc(txt);
	}
	
	if(label[0] == RETURN_EOF)
		return RETURN_EOF;
	if(label[0] == '#')
		return RETURN_COMMENT;
	
	if(label[0] == 'S'){		// S
		label[1] = getc(txt);	// P
		label[2] = getc(txt);	// R
		label[3] = getc(txt);	// I
		label[4] = getc(txt);	// T
		label[5] = getc(txt);	// E
		label[6] = getc(txt);	// S
		label[7] = getc(txt);	// :
		label[8] = '\0';		// \0
		
		if(strcmp(label, "SPRITES:") == 0)
			return LABEL_SPRITES;
	}
	else if(label[0] == 'A'){	// A
		label[1] = getc(txt);	// N
		label[2] = getc(txt);	// I
		label[3] = getc(txt);	// M
		label[4] = getc(txt);	// S
		label[5] = getc(txt);	// :
		label[6] = '\0';		// \0
		
		if(strcmp(label, "ANIMS:") == 0)
			return LABEL_ANIMS;
	}
	
	return RETURN_ERROR;
}



FindMacro(){
	char label[4];
	
	label[0] = getc(txt);
	label[1] = getc(txt);
	label[2] = getc(txt);
	if(label[0] == 'E' && label[1] == 'N' && label[2] == 'D'){
		label[3] = getc(txt);
		while(label[3] == 0x20 || label[3] == 0x9)
			label[3] = getc(txt);
		
		if(label[3] == 0xD || label[3] == 0xA)
			return RETURN_M_END;
	}
	
	return RETURN_ERROR;
}



int main(int argc, char **argv){
	int l;
	int b;
	int anim_size = 0;	// We MUST initialize this with a value
	int v[256];
	int v1;
	int v2;
	int v3;
	int v4;
	int x;
	int y;
	
	char filename[256];
	
	allegro_init();
	
	if(argc < 2){
		allegro_message("Must run from the command prompt or run dialog:\n\n"
						"\"SPRITE COMPILER\" \"[file without extension]\"");
		allegro_exit();
		return 0;
	}
	
	set_color_depth(24);
	
	strcpy(filename, argv[1]);
	strcat(filename, ".txt");
	txt = fopen(filename, "rb");
	if(!txt){
		allegro_message("Could not open \"%s\"", filename);
		allegro_exit();
		return 0;
	}
	
	strcpy(filename, argv[1]);
	strcat(filename, ".bmp");
	bmp = load_bitmap(filename, NULL);
	if(!bmp){
		allegro_message("Could not open \"%s\"", filename);
		fclose(txt);
		allegro_exit();
		return 0;
	}
	
	strcpy(filename, argv[1]);
	strcat(filename, ".psf");
	psf = fopen(filename, "w+b");
	if(!psf){
		allegro_message("Could not create \"%s\"", filename);
		fclose(txt);
		destroy_bitmap(bmp);
		allegro_exit();
		return 0;
	}

	while(l != RETURN_EOF){
		l = FindLabel();
		while(l == RETURN_COMMENT){
			b = 0;
			while(b != 0xA && b != 0xD)
				b = getc(txt);
			l = FindLabel();
		}
		
		if(l == LABEL_SPRITES){
			sprite_count = FindValue();
			sprite = realloc(sprite, sprite_count << 3);
			
			l = FindSub();
			while(l == RETURN_COMMENT){
				b = 0;
				while(b != 0xA && b != 0xD)
					b = getc(txt);
				l = FindSub();
			}
			while(l != RETURN_M){
				v1 = FindValue();
				v3 = FindValue();
				
				v2 = FindValue();
				v4 = FindValue();
				
				if(v2 > 0)
					v2 += (v1-1);
				if(v4 > 0)
					v4 += (v3-1);
				
				sprite[l].size_x_left = v1 + ((orientation & 1) << 15);
				sprite[l].size_x_right = v2;
				sprite[l].size_y_top = v3 + ((orientation & 2) << 14);
				sprite[l].size_y_bottom = v4;
				
				l = FindSub();
				while(l == RETURN_COMMENT){
					b = 0;
					while(b != 0xA && b != 0xD)
						b = getc(txt);
					l = FindSub();
				}
			}
			if(l == RETURN_M){
				l = FindMacro();
				if(l == RETURN_ERROR)
					allegro_message("Invalid macro!");
				//else if(l == RETURN_M_END)
				//	break;
			}
		}
		else if(l == LABEL_ANIMS){
			anim_count = FindValue();
			//sprite = realloc(sprite, sprite_count << 3);
			
			l = FindSub();
			while(l == RETURN_COMMENT){
				b = 0;
				while(b != 0xA && b != 0xD)
					b = getc(txt);
				l = FindSub();
			}
			while(l != RETURN_M){
				for(b=0; b < 256; b++)
					v[b] = 0;
				
				v[0] = FindValue();
				if(v[0] > 255){
					allegro_message("Animation speed cannot exceed 0xFF!");
					free(sprite);
					free(anim);
					fclose(psf);
					fclose(txt);
					destroy_bitmap(bmp);
					allegro_exit();
					return 0;
				}
				
				for(b=1; v[b-1] != RETURN_COMMENT; b++){
					if(b > 255){
						allegro_message("Animation 0x%X is too long!", l);
						free(sprite);
						free(anim);
						fclose(psf);
						fclose(txt);
						destroy_bitmap(bmp);
						allegro_exit();
						return 0;
					}
					
					v[b] = FindValue();
				}
				
				if(b < 2){
					allegro_message("No speed specified for animation 0x%X!", l);
					free(sprite);
					free(anim);
					fclose(psf);
					fclose(txt);
					destroy_bitmap(bmp);
					allegro_exit();
					return 0;
				}
				
				b--;
				anim_size += b;
				anim = realloc(anim, anim_size << 1);
				
				anim[anim_size - b] = b-1 + (v[0] << 8);
				for(x=1; x < b; x++)
					anim[anim_size - b + x] = v[x];
				
				l = FindSub();
				while(l == RETURN_COMMENT){
					b = 0;
					while(b != 0xA && b != 0xD)
						b = getc(txt);
					l = FindSub();
				}
			}
			if(l == RETURN_M){
				l = FindMacro();
				if(l == RETURN_ERROR)
					allegro_message("Invalid macro!");
				//else if(l == RETURN_M_END)
				//	break;
			}
		}
	}
	
	fwrite(&sprite_count, 2, 1, psf);
	fwrite(&anim_count, 2, 1, psf);
	fwrite(&bmp->w, 2, 1, psf);
	fwrite(&bmp->h, 2, 1, psf);
	
	for(b=0; b < sprite_count; b++){
		fwrite(&sprite[b].size_x_left, 2, 1, psf);
		fwrite(&sprite[b].size_x_right, 2, 1, psf);
		fwrite(&sprite[b].size_y_top, 2, 1, psf);
		fwrite(&sprite[b].size_y_bottom, 2, 1, psf);
	}
	
	for(b=0; b < anim_size; b++)
		fwrite(&anim[b], 2, 1, psf);
	
	for(y=0; y < bmp->h; y++){
		for(x=0; x < bmp->w; x++){
			/*v1 = 0;
			v2 = bmp->line[y][x*3] >> 2;
			v3 = bmp->line[y][(x*3)+1] >> 2;
			v4 = bmp->line[y][(x*3)+2] >> 2;
			if(v2 == 0x3F && v3 == 0 && v4 == 0x3F){
				v2 = 0xFF;
				v3 = 0xFF;
				v4 = 0xFF;
			}
			fwrite(&v1, 1, 1, psf);
			fwrite(&v2, 1, 1, psf);
			fwrite(&v3, 1, 1, psf);
			fwrite(&v4, 1, 1, psf);*/
			
			v1 = bmp->line[y][x*3] >> 3;
			v2 = bmp->line[y][(x*3)+1] >> 2;
			v3 = bmp->line[y][(x*3)+2] >> 3;
			v4 = (v1 << 11) | (v2 << 5) | v3;
			//v4 = (v4 << 8) | (v4 >> 8);
			fwrite(&v4, 2, 1, psf);
		}
	}
	
	allegro_message("Sprite sheet \"%s\" successfully created!", filename);
	
	fclose(psf);
	fclose(txt);
	destroy_bitmap(bmp);
	allegro_exit();
	return 0;
}
END_OF_MAIN();
